use base64::Engine;
use sea_orm::entity::prelude::*;
use std::fmt::{Debug, Display, Formatter};

const ERR_MSG_INVALID_ADDRESS_LENGTH: &str = "无效的ip地址长度";

#[derive(Default, Clone, Debug, PartialEq, Eq, DeriveValueType)]
pub struct IpAddr(pub Vec<u8>);

impl IpAddr {
    pub fn as_slice(&self) -> &[u8] {
        self.0.as_slice()
    }
}

impl From<&std::net::IpAddr> for IpAddr {
    fn from(value: &std::net::IpAddr) -> Self {
        match value {
            std::net::IpAddr::V4(addr) => IpAddr(addr.octets().to_vec()),
            std::net::IpAddr::V6(addr) => IpAddr(addr.octets().to_vec()),
        }
    }
}

impl From<std::net::IpAddr> for IpAddr {
    fn from(value: std::net::IpAddr) -> Self {
        Self::from(&value)
    }
}

impl TryFrom<&IpAddr> for std::net::IpAddr {
    type Error = anyhow::Error;

    fn try_from(value: &IpAddr) -> Result<Self, Self::Error> {
        match value.0.len() {
            4 => {
                let addr: [u8; 4] = value.0.as_slice().try_into().map_err(anyhow::Error::new)?;
                Ok(std::net::IpAddr::V4(std::net::Ipv4Addr::from(addr)))
            }
            16 => {
                let addr: [u8; 16] = value.0.as_slice().try_into().map_err(anyhow::Error::new)?;
                Ok(std::net::IpAddr::V6(std::net::Ipv6Addr::from(addr)))
            }
            _ => Err(anyhow::anyhow!("{}", ERR_MSG_INVALID_ADDRESS_LENGTH)),
        }
    }
}

impl TryFrom<IpAddr> for std::net::IpAddr {
    type Error = anyhow::Error;
    fn try_from(value: IpAddr) -> Result<Self, Self::Error> {
        Self::try_from(&value)
    }
}

impl TryFrom<&[u8]> for IpAddr {
    type Error = anyhow::Error;
    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        let length = value.len();
        match length {
            4 | 16 => Ok(Self(value.to_vec())),
            _ => Err(anyhow::anyhow!("{}", ERR_MSG_INVALID_ADDRESS_LENGTH)),
        }
    }
}

#[derive(Default, Clone, PartialEq, Eq, DeriveValueType)]
pub struct Ipv4Addr(pub u32);

impl From<&std::net::Ipv4Addr> for Ipv4Addr {
    fn from(value: &std::net::Ipv4Addr) -> Self {
        Ipv4Addr(value.to_bits())
    }
}

impl From<std::net::Ipv4Addr> for Ipv4Addr {
    fn from(value: std::net::Ipv4Addr) -> Self {
        Self::from(&value)
    }
}

impl From<&Ipv4Addr> for std::net::Ipv4Addr {
    fn from(value: &Ipv4Addr) -> Self {
        std::net::Ipv4Addr::from(value.0)
    }
}

impl From<Ipv4Addr> for std::net::Ipv4Addr {
    fn from(value: Ipv4Addr) -> Self {
        Self::from(&value)
    }
}

impl Display for Ipv4Addr {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let ip = std::net::Ipv4Addr::from(self.0);
        f.write_str(ip.to_string().as_str())
    }
}

impl Debug for Ipv4Addr {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        Display::fmt(self, f)
    }
}

#[derive(Default, Clone, PartialEq, Eq, DeriveValueType)]
pub struct Ipv6Addr(pub Vec<u8>);

impl From<&std::net::Ipv6Addr> for Ipv6Addr {
    fn from(value: &std::net::Ipv6Addr) -> Self {
        Ipv6Addr(value.to_bits().to_be_bytes().to_vec())
    }
}

impl From<std::net::Ipv6Addr> for Ipv6Addr {
    fn from(value: std::net::Ipv6Addr) -> Self {
        Self::from(&value)
    }
}

impl TryFrom<&Ipv6Addr> for std::net::Ipv6Addr {
    type Error = anyhow::Error;

    fn try_from(value: &Ipv6Addr) -> Result<Self, Self::Error> {
        wanmesh_common::util::be_bytes_try_to_ipv6addr(value.0.as_slice())
    }
}

impl TryFrom<Ipv6Addr> for std::net::Ipv6Addr {
    type Error = anyhow::Error;

    fn try_from(value: Ipv6Addr) -> Result<Self, Self::Error> {
        wanmesh_common::util::be_bytes_try_to_ipv6addr(value.0.as_slice())
    }
}

impl Display for Ipv6Addr {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let ip = match std::net::Ipv6Addr::try_from(self) {
            Ok(ip) => ip,
            Err(_) => {
                let arr: Vec<String> = self.0.iter().map(|x| format!("0x{:02x}", x)).collect();
                let s = format!("[{}]", arr.join(", "));
                return f.write_str(s.as_str());
            }
        };
        f.write_str(ip.to_string().as_str())
    }
}

impl Debug for Ipv6Addr {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        Display::fmt(self, f)
    }
}

#[derive(Default, Debug, Clone)]
pub struct OptionIpv4Inet {
    pub address: Option<Ipv4Addr>,
    pub netmask: Option<u8>,
}

#[derive(Default, Debug, Clone)]
pub struct OptionIpv6Inet {
    pub address: Option<Ipv6Addr>,
    pub netmask: Option<u8>,
}

#[derive(Default, Debug, Clone)]
pub struct OptionIpInet {
    pub ipv4: OptionIpv4Inet,
    pub ipv6: OptionIpv6Inet,
}

impl From<&cidr::IpInet> for OptionIpInet {
    fn from(value: &cidr::IpInet) -> Self {
        let mut ipv4_address: Option<Ipv4Addr> = None;
        let mut ipv4_netmask = None;
        let mut ipv6_address: Option<Ipv6Addr> = None;
        let mut ipv6_netmask = None;

        match value {
            cidr::IpInet::V4(ip) => {
                ipv4_address = Some(Ipv4Addr::from(&ip.address()));
                ipv4_netmask = Some(ip.network_length());
            }
            cidr::IpInet::V6(ip) => {
                ipv6_address = Some(Ipv6Addr::from(&ip.address()));
                ipv6_netmask = Some(ip.network_length());
            }
        }

        OptionIpInet {
            ipv4: OptionIpv4Inet {
                address: ipv4_address,
                netmask: ipv4_netmask,
            },
            ipv6: OptionIpv6Inet {
                address: ipv6_address,
                netmask: ipv6_netmask,
            },
        }
    }
}

#[derive(Default, Clone, PartialEq, Eq, DeriveValueType)]
pub struct Base64Bytes(pub Vec<u8>);

impl Base64Bytes {
    pub fn as_slice(&self) -> &[u8] {
        self.0.as_slice()
    }

    pub fn to_vec(&self) -> Vec<u8> {
        self.0.clone()
    }

    pub fn to_base64_string(&self) -> String {
        base64::prelude::BASE64_STANDARD.encode(self.0.as_slice())
    }
}

impl From<&[u8]> for Base64Bytes {
    fn from(value: &[u8]) -> Self {
        Base64Bytes(value.to_vec())
    }
}

impl Display for Base64Bytes {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let s = base64::prelude::BASE64_STANDARD.encode(self.0.as_slice());
        f.write_str(s.as_str())
    }
}

impl Debug for Base64Bytes {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        Display::fmt(self, f)
    }
}

pub type ActiveValue<T> = sea_orm::ActiveValue<T>;

pub trait ActiveModelExt {
    fn create_timestamp_millis(&self) -> ActiveValue<u64>;
    fn update_timestamp_millis(&self) -> ActiveValue<u64>;
    fn set_create_timestamp_millis(&mut self, create_timestamp_millis: ActiveValue<u64>);
    fn set_update_timestamp_millis(&mut self, update_timestamp_millis: ActiveValue<u64>);
}

#[macro_export]
macro_rules! impl_db_po_active_model_ext {
    ($($struct_name:ty),+) => {
        $(
            impl ActiveModelExt for $struct_name {
                fn create_timestamp_millis(&self) -> po::ActiveValue<u64> {
                    self.create_timestamp_millis.clone()
                }

                fn update_timestamp_millis(&self) -> po::ActiveValue<u64> {
                    self.update_timestamp_millis.clone()
                }

                fn set_create_timestamp_millis(&mut self, create_timestamp_millis: po::ActiveValue<u64>) {
                    self.create_timestamp_millis = create_timestamp_millis;
                }

                fn set_update_timestamp_millis(&mut self, update_timestamp_millis: po::ActiveValue<u64>) {
                    self.update_timestamp_millis = update_timestamp_millis;
                }
            }
        )+
    };
}

pub trait ColumnExt {
    fn id() -> Self;
}

#[macro_export]
macro_rules! impl_db_po_column_ext {
     ($($struct_name:ty),+) => {
        $(
            impl ColumnExt for $struct_name {
                 fn id() -> Self {
                    Self::Id.clone()
                 }
            }
        )+
    };
}

pub fn option_to_active_value<T, R>(o: Option<T>) -> ActiveValue<R>
where
    R: From<T> + Into<Value>,
{
    match o {
        Some(v) => ActiveValue::Set(R::from(v)),
        None => ActiveValue::NotSet,
    }
}

pub fn nullable_option_to_active_value<T, R>(o: Option<Option<T>>) -> ActiveValue<Option<R>>
where
    R: From<T> + Into<Value> + sea_query::Nullable,
{
    match o {
        Some(oo) => match oo {
            Some(v) => ActiveValue::Set(Some(R::from(v))),
            None => ActiveValue::Set(None),
        },
        None => ActiveValue::NotSet,
    }
}
